<?php

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
use app\common\model\common\PartitionPackage;

class ExecuteInsertSql extends Command
{
    // 批量插入的批次大小
    const BATCH_SIZE = 500;
    
    // 批量检查存在性的批次大小
    const CHECK_BATCH_SIZE = 1000;
    
    // 缓存表的主键信息
    private $primaryKeyCache = [];
    
    protected function configure()
    {
        $this->setName('once:ExecuteInsertSql')
            ->setDescription('执行extend/database/insert目录下的INSERT SQL文件')
            ->addArgument('partition_id', Argument::REQUIRED, '分区ID')
            ->addArgument('sql_file', Argument::OPTIONAL, '指定要执行的SQL文件名（可选，必须在insert目录内）');
    }

    protected function execute(Input $input, Output $output)
    {
        $partitionId = $input->getArgument('partition_id') ?? '';
        $sqlFileName = $input->getArgument('sql_file') ?? '';
        
        if (empty($partitionId)) {
            $output->writeln('<error>分区ID不能为空</error>');
            return;
        }

        // 获取分区信息
        $partitionInfo = PartitionPackage::where([
            'partition_id' => $partitionId,
            'status' => 1
        ])->find();

        if (empty($partitionInfo)) {
            $output->writeln('<error>分区ID无效：' . $partitionId . '</error>');
            return;
        }

        $output->writeln('<info>开始执行INSERT SQL文件...</info>');
        $output->writeln('<info>分区ID：' . $partitionId . '</info>');
        $output->writeln('<info>分区名称：' . $partitionInfo['partition_name'] . '</info>');
        $output->writeln('<info>数据库：' . $partitionInfo['api_mysql_database'] . '</info>');
        $output->writeln('<info>批量插入大小：' . self::BATCH_SIZE . ' 条/批次</info>');
        $output->writeln('<info>批量检查大小：' . self::CHECK_BATCH_SIZE . ' 条/批次</info>');

        try {
            // 建立数据库连接
            $output->writeln('<comment>正在连接数据库...</comment>');
            $pdo = $this->createDatabaseConnection($partitionInfo);
            $output->writeln('<info>数据库连接成功</info>');
            
            // 扫描SQL文件目录
            $sqlDir = root_path() . 'extend/database/insert/';
            
            if (!is_dir($sqlDir)) {
                $output->writeln('<error>SQL文件目录不存在：' . $sqlDir . '</error>');
                return;
            }

            // 处理SQL文件
            $sqlFiles = [];
            
            if (!empty($sqlFileName)) {
                // 指定了特定的SQL文件
                $specifiedFile = $sqlDir . $sqlFileName;
                
                // 确保文件名以.sql结尾
                if (!str_ends_with($sqlFileName, '.sql')) {
                    $specifiedFile .= '.sql';
                    $sqlFileName .= '.sql';
                }
                
                // 验证文件是否存在且在insert目录内
                $realSqlDir = realpath($sqlDir);
                $realSpecifiedFile = realpath($specifiedFile);
                
                if (!$realSpecifiedFile) {
                    $output->writeln('<error>指定的SQL文件不存在：' . $sqlFileName . '</error>');
                    return;
                }
                
                // 安全检查：确保文件在insert目录内
                if (!str_starts_with($realSpecifiedFile, $realSqlDir)) {
                    $output->writeln('<error>指定的SQL文件必须在insert目录内：' . $sqlFileName . '</error>');
                    return;
                }
                
                $sqlFiles = [$specifiedFile];
                $output->writeln('<info>指定执行文件：' . $sqlFileName . '</info>');
            } else {
                // 扫描所有SQL文件
                $sqlFiles = glob($sqlDir . '*.sql');
                
                if (empty($sqlFiles)) {
                    $output->writeln('<comment>未找到SQL文件</comment>');
                    return;
                }
                
                $output->writeln('<info>找到 ' . count($sqlFiles) . ' 个SQL文件</info>');
            }

            $output->writeln(str_repeat('=', 60));

            $totalInserted = 0;
            $totalSkipped = 0;
            $totalErrors = 0;

            foreach ($sqlFiles as $index => $sqlFile) {
                $fileName = basename($sqlFile);
                $fileSize = round(filesize($sqlFile) / 1024, 2) . 'KB';
                $currentFile = $index + 1;
                
                if (count($sqlFiles) > 1) {
                    $output->writeln('<info>[' . $currentFile . '/' . count($sqlFiles) . '] 处理文件：' . $fileName . ' (' . $fileSize . ')</info>');
                } else {
                    $output->writeln('<info>处理文件：' . $fileName . ' (' . $fileSize . ')</info>');
                }
                
                // 从文件名推断表名（去掉.sql后缀）
                $tableName = pathinfo($fileName, PATHINFO_FILENAME);
                $output->writeln('<comment>  目标表：' . $tableName . '</comment>');
                
                // 读取SQL文件内容
                $sqlContent = file_get_contents($sqlFile);
                
                if (empty($sqlContent)) {
                    $output->writeln('<comment>  文件为空，跳过</comment>');
                    continue;
                }

                // 处理表的数据插入（优化版批量模式）
                $result = $this->processTableInsertsOptimized($pdo, $tableName, $sqlContent, $output);
                
                $totalInserted += $result['inserted'];
                $totalSkipped += $result['skipped'];
                $totalErrors += $result['errors'];
                
                $output->writeln(str_repeat('-', 40));
            }

            $output->writeln(str_repeat('=', 60));
            $output->writeln('<info>执行完成！</info>');
            $output->writeln('<info>总计 - 新增：' . $totalInserted . '，跳过：' . $totalSkipped . '，错误：' . $totalErrors . '</info>');

        } catch (\Exception $e) {
            $output->writeln('<error>执行失败：' . $e->getMessage() . '</error>');
        }
    }

    /**
     * 创建数据库连接
     */
    private function createDatabaseConnection($partitionInfo)
    {
        $host = $partitionInfo['api_mysql_host_name'];
        $username = $partitionInfo['api_mysql_username'];
        $password = $partitionInfo['api_mysql_password'];
        $port = $partitionInfo['api_mysql_hostport'];
        $database = $partitionInfo['api_mysql_database'];

        $dsn = "mysql:host={$host};port={$port};dbname={$database};charset=utf8mb4";
        
        return new \PDO($dsn, $username, $password, [
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
        ]);
    }

    /**
     * 处理表的数据插入（优化版批量模式）
     */
    private function processTableInsertsOptimized($pdo, $tableName, $sqlContent, $output)
    {
        $insertedCount = 0;
        $skippedCount = 0;
        $errorCount = 0;

        try {
            // 从SQL内容中提取INSERT语句
            $insertStatements = $this->extractInsertStatements($sqlContent, $tableName);
            
            if (empty($insertStatements)) {
                $output->writeln('<comment>  未找到INSERT语句</comment>');
                return ['inserted' => 0, 'skipped' => 0, 'errors' => 0];
            }

            $output->writeln('<info>  找到 ' . count($insertStatements) . ' 条INSERT语句</info>');

            // 获取主键
            $primaryKey = $this->getPrimaryKey($pdo, $tableName);
            
            if (!$primaryKey) {
                $output->writeln('<comment>  表 ' . $tableName . ' 没有主键，使用INSERT IGNORE模式</comment>');
                return $this->processWithoutPrimaryKey($pdo, $tableName, $insertStatements, $output);
            }
            
            $output->writeln('<info>  表主键：' . $primaryKey . '</info>');
            
            // 流式处理INSERT语句，避免一次性加载所有数据到内存
            $allInsertData = [];
            $processedCount = 0;
            $totalStatements = count($insertStatements);
            
            foreach ($insertStatements as $insertSql) {
                $insertData = $this->parseInsertStatement($insertSql);
                if ($insertData) {
                    $allInsertData[] = $insertData;
                    $processedCount++;
                    
                    // 每处理1000条就进行一次批量检查和插入，释放内存
                    if (count($allInsertData) >= 1000) {
                        $result = $this->processBatch($pdo, $tableName, $primaryKey, $allInsertData, $output);
                        $insertedCount += $result['inserted'];
                        $skippedCount += $result['skipped'];
                        $errorCount += $result['errors'];
                        
                        // 清空数组释放内存
                        $allInsertData = [];
                        
                        // 强制垃圾回收
                        if ($processedCount % 5000 === 0) {
                            gc_collect_cycles();
                            $output->writeln('<comment>  已处理：' . $processedCount . '/' . $totalStatements . ' 条SQL语句</comment>');
                        }
                    }
                }
            }
            
            // 处理剩余的数据
            if (!empty($allInsertData)) {
                $result = $this->processBatch($pdo, $tableName, $primaryKey, $allInsertData, $output);
                $insertedCount += $result['inserted'];
                $skippedCount += $result['skipped'];
                $errorCount += $result['errors'];
            }
            
            $output->writeln('<info>  完成处理：新增：' . $insertedCount . '，跳过：' . $skippedCount . '，错误：' . $errorCount . '</info>');

        } catch (\Exception $e) {
            $output->writeln('<error>  处理表失败：' . $e->getMessage() . '</error>');
            $errorCount++;
        }

        return ['inserted' => $insertedCount, 'skipped' => $skippedCount, 'errors' => $errorCount];
    }
    
    /**
     * 处理单个批次
     */
    private function processBatch($pdo, $tableName, $primaryKey, $allInsertData, $output)
    {
        $insertedCount = 0;
        $skippedCount = 0;
        $errorCount = 0;
        
        // 批量检查数据是否存在
        $output->writeln('<comment>  开始批量检查数据存在性...</comment>');
        $existingIds = $this->batchCheckDataExists($pdo, $tableName, $primaryKey, $allInsertData, $output);
        
        // 使用关联数组提高查找效率
        $existingIdsMap = array_flip($existingIds);
        
        // 过滤出不存在的数据
        $newData = [];
        foreach ($allInsertData as $insertData) {
            $pkIndex = array_search($primaryKey, $insertData['fields']);
            if ($pkIndex !== false && isset($insertData['values'][$pkIndex])) {
                $pkValue = $insertData['values'][$pkIndex];
                if (!isset($existingIdsMap[$pkValue])) {
                    $newData[] = $insertData;
                } else {
                    $skippedCount++;
                }
            }
        }
        
        $output->writeln('<info>  数据统计 - 新增：' . count($newData) . '，跳过：' . $skippedCount . '</info>');
        
        if (empty($newData)) {
            $output->writeln('<comment>  所有数据已存在，无需插入</comment>');
            return ['inserted' => 0, 'skipped' => $skippedCount, 'errors' => $errorCount];
        }
        
        // 分批插入新数据
        $batches = array_chunk($newData, self::BATCH_SIZE);
        $totalBatches = count($batches);
        
        $output->writeln('<info>  开始批量插入，共 ' . $totalBatches . ' 个批次</info>');
        
        foreach ($batches as $batchIndex => $batch) {
            $currentBatch = $batchIndex + 1;
            $batchSize = count($batch);
            
            $output->writeln('<comment>  插入批次 ' . $currentBatch . '/' . $totalBatches . ' (' . $batchSize . ' 条)...</comment>');
            
            try {
                $insertedInBatch = $this->executeBatchInsert($pdo, $tableName, $batch, $output);
                $insertedCount += $insertedInBatch;
                $output->writeln('<info>    ✓ 批量插入成功：' . $insertedInBatch . ' 条</info>');
            } catch (\Exception $e) {
                $output->writeln('<error>    ✗ 批量插入失败：' . $e->getMessage() . '</error>');
                $output->writeln('<comment>    尝试逐条插入...</comment>');
                
                // 批量插入失败时，逐条插入
                foreach ($batch as $dataIndex => $insertData) {
                    try {
                        $this->executeInsertStatement($pdo, $insertData);
                        $insertedCount++;
                    } catch (\Exception $singleError) {
                        $errorCount++;
                        $output->writeln('<error>      第' . ($dataIndex + 1) . '条插入失败：' . $singleError->getMessage() . '</error>');
                    }
                }
            }
        }
        
        return ['inserted' => $insertedCount, 'skipped' => $skippedCount, 'errors' => $errorCount];
    }

    /**
     * 批量检查数据是否存在
     */
    private function batchCheckDataExists($pdo, $tableName, $primaryKey, $allInsertData, $output)
    {
        $existingIds = [];
        
        // 提取所有主键值
        $allPkValues = [];
        foreach ($allInsertData as $insertData) {
            $pkIndex = array_search($primaryKey, $insertData['fields']);
            if ($pkIndex !== false && isset($insertData['values'][$pkIndex])) {
                $allPkValues[] = $insertData['values'][$pkIndex];
            }
        }
        
        if (empty($allPkValues)) {
            return $existingIds;
        }
        
        // 去重
        $allPkValues = array_unique($allPkValues);
        $output->writeln('<comment>    需要检查的唯一主键值：' . count($allPkValues) . ' 个</comment>');
        
        // 分批检查
        $checkBatches = array_chunk($allPkValues, self::CHECK_BATCH_SIZE);
        
        foreach ($checkBatches as $batchIndex => $pkBatch) {
            $currentBatch = $batchIndex + 1;
            $output->writeln('<comment>    检查批次 ' . $currentBatch . '/' . count($checkBatches) . ' (' . count($pkBatch) . ' 个ID)...</comment>');
            
            // 构建IN查询
            $placeholders = str_repeat('?,', count($pkBatch) - 1) . '?';
            $sql = "SELECT `{$primaryKey}` FROM `{$tableName}` WHERE `{$primaryKey}` IN ({$placeholders})";
            
            $stmt = $pdo->prepare($sql);
            $stmt->execute($pkBatch);
            
            $batchExisting = $stmt->fetchAll(\PDO::FETCH_COLUMN);
            $existingIds = array_merge($existingIds, $batchExisting);
            
            $output->writeln('<comment>      找到已存在ID：' . count($batchExisting) . ' 个</comment>');
        }
        
        $output->writeln('<info>    总计已存在ID：' . count($existingIds) . ' 个</info>');
        
        return $existingIds;
    }

    /**
     * 处理没有主键的表（使用INSERT IGNORE）
     */
    private function processWithoutPrimaryKey($pdo, $tableName, $insertStatements, $output)
    {
        $insertedCount = 0;
        $errorCount = 0;
        
        // 解析所有INSERT语句
        $allInsertData = [];
        foreach ($insertStatements as $insertSql) {
            $insertData = $this->parseInsertStatement($insertSql);
            if ($insertData) {
                $allInsertData[] = $insertData;
            }
        }
        
        // 分批插入
        $batches = array_chunk($allInsertData, self::BATCH_SIZE);
        $totalBatches = count($batches);
        
        $output->writeln('<info>  开始INSERT IGNORE批量插入，共 ' . $totalBatches . ' 个批次</info>');

        foreach ($batches as $batchIndex => $batch) {
            $currentBatch = $batchIndex + 1;
            $batchSize = count($batch);
            
            $output->writeln('<comment>  插入批次 ' . $currentBatch . '/' . $totalBatches . ' (' . $batchSize . ' 条)...</comment>');
            
            try {
                $insertedInBatch = $this->executeBatchInsertIgnore($pdo, $tableName, $batch);
                $insertedCount += $insertedInBatch;
                $output->writeln('<info>    ✓ INSERT IGNORE成功：' . $insertedInBatch . ' 条</info>');
            } catch (\Exception $e) {
                $output->writeln('<error>    ✗ INSERT IGNORE失败：' . $e->getMessage() . '</error>');
                $errorCount += count($batch);
            }
        }
        
        return ['inserted' => $insertedCount, 'skipped' => 0, 'errors' => $errorCount];
    }

    /**
     * 执行批量INSERT IGNORE
     */
    private function executeBatchInsertIgnore($pdo, $tableName, $batchData)
    {
        if (empty($batchData)) {
            return 0;
        }

        // 获取第一条数据的字段结构
        $firstData = $batchData[0];
        $fields = $firstData['fields'];
        
        // 验证所有数据的字段结构是否一致
        foreach ($batchData as $data) {
            if ($data['fields'] !== $fields) {
                throw new \Exception('批次中数据字段结构不一致，无法批量插入');
            }
        }

        // 构建批量INSERT IGNORE SQL
        $fieldsList = '`' . implode('`, `', $fields) . '`';
        $placeholders = '(' . str_repeat('?,', count($fields) - 1) . '?)';
        $allPlaceholders = str_repeat($placeholders . ',', count($batchData) - 1) . $placeholders;
        
        $sql = "INSERT IGNORE INTO `{$tableName}` ({$fieldsList}) VALUES {$allPlaceholders}";
        
        // 准备所有值
        $allValues = [];
        foreach ($batchData as $data) {
            $allValues = array_merge($allValues, $data['values']);
        }
        
        // 执行批量插入
        $stmt = $pdo->prepare($sql);
        $stmt->execute($allValues);
        
        // 返回实际插入的行数
        return $stmt->rowCount();
    }

    /**
     * 获取表的主键字段（带缓存）
     */
    private function getPrimaryKey($pdo, $tableName)
    {
        if (isset($this->primaryKeyCache[$tableName])) {
            return $this->primaryKeyCache[$tableName];
        }
        
        try {
            $stmt = $pdo->prepare("SHOW KEYS FROM `{$tableName}` WHERE Key_name = 'PRIMARY'");
            $stmt->execute();
            $result = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            $primaryKey = $result ? $result['Column_name'] : null;
            $this->primaryKeyCache[$tableName] = $primaryKey;
            
            return $primaryKey;
            
        } catch (\PDOException $e) {
            $this->primaryKeyCache[$tableName] = null;
            return null;
        }
    }

    /**
     * 从SQL内容中提取指定表的INSERT语句
     */
    private function extractInsertStatements($sqlContent, $tableName)
    {
        $insertStatements = [];
        
        // 使用更精确的方法来提取INSERT语句
        // 先按行分割，然后逐行处理
        $lines = explode("\n", $sqlContent);
        $currentStatement = '';
        $inStatement = false;
        
        foreach ($lines as $line) {
            $line = trim($line);
            
            // 跳过空行和注释
            if (empty($line) || strpos($line, '--') === 0 || strpos($line, '#') === 0) {
                continue;
            }
            
            // 检查是否是目标表的INSERT语句开始
            if (preg_match('/^INSERT\s+INTO\s+`?' . preg_quote($tableName, '/') . '`?\s*\(/i', $line)) {
                $inStatement = true;
                $currentStatement = $line;
            } elseif ($inStatement) {
                $currentStatement .= ' ' . $line;
            }
            
            // 检查语句是否结束（以分号结尾）
            if ($inStatement && (substr(rtrim($line), -1) === ';' || substr(rtrim($line), -2) === ');')) {
                $insertStatements[] = trim($currentStatement);
                $currentStatement = '';
                $inStatement = false;
            }
        }
        
        // 如果最后一个语句没有分号结尾，也添加进去
        if ($inStatement && !empty(trim($currentStatement))) {
            $insertStatements[] = trim($currentStatement);
        }
        
        return $insertStatements;
    }

    /**
     * 解析INSERT语句，提取字段和值
     */
    private function parseInsertStatement($insertSql)
    {
        $insertSql = trim($insertSql);
        
        // 移除末尾的分号
        $insertSql = rtrim($insertSql, ';');
        
        // 找到VALUES关键字的位置
        $valuesPos = stripos($insertSql, 'VALUES');
        if ($valuesPos === false) {
            return false;
        }
        
        // 提取INSERT部分和VALUES部分
        $insertPart = substr($insertSql, 0, $valuesPos);
        $valuesPart = substr($insertSql, $valuesPos + 6); // 6 = strlen('VALUES')
        
        // 解析表名和字段
        if (!preg_match('/INSERT\s+INTO\s+`?(\w+)`?\s*\(([^)]+)\)/i', $insertPart, $matches)) {
            return false;
        }
        
        $tableName = $matches[1];
        $fieldsStr = $matches[2];
        
        // 解析字段名
        $fields = array_map(function($field) {
            return trim($field, '` ');
        }, explode(',', $fieldsStr));
        
        // 清理VALUES部分，移除前后空白
        $valuesPart = trim($valuesPart);
        
        // 使用更精确的方法来提取VALUES括号内的内容
        $openParenPos = strpos($valuesPart, '(');
        $closeParenPos = strrpos($valuesPart, ')');
        
        if ($openParenPos === false || $closeParenPos === false || $closeParenPos <= $openParenPos) {
            return false;
        }
        
        $valuesStr = substr($valuesPart, $openParenPos + 1, $closeParenPos - $openParenPos - 1);
        
        // 解析值
        $values = $this->parseInsertValues($valuesStr);
        
        // 验证字段和值的数量是否匹配
        if (count($fields) !== count($values)) {
            // 提供更详细的调试信息
            $debugInfo = [
                'sql' => strlen($insertSql) > 200 ? substr($insertSql, 0, 200) . '...' : $insertSql,
                'fields_count' => count($fields),
                'values_count' => count($values),
                'fields' => $fields,
                'values_preview' => array_map(function($v) { 
                    return strlen($v) > 50 ? substr($v, 0, 50) . '...' : $v; 
                }, $values)
            ];
            
            throw new \Exception("字段数量(" . count($fields) . ")与值数量(" . count($values) . ")不匹配。调试信息：" . json_encode($debugInfo, JSON_UNESCAPED_UNICODE));
        }
        
        return [
            'table' => $tableName,
            'fields' => $fields,
            'values' => $values
        ];
    }

    /**
     * 优化的INSERT值解析方法
     */
    private function parseInsertValues($valuesStr)
    {
        $values = [];
        $current = '';
        $inQuotes = false;
        $quoteChar = '';
        $parenthesesLevel = 0;
        $length = strlen($valuesStr);
        $i = 0;
        
        while ($i < $length) {
            $char = $valuesStr[$i];
            
            if (!$inQuotes) {
                if ($char === "'" || $char === '"') {
                    // 开始引号
                    $inQuotes = true;
                    $quoteChar = $char;
                    $current .= $char;
                } elseif ($char === '(') {
                    // 进入括号
                    $parenthesesLevel++;
                    $current .= $char;
                } elseif ($char === ')') {
                    // 退出括号
                    $parenthesesLevel--;
                    $current .= $char;
                } elseif ($char === ',' && $parenthesesLevel === 0) {
                    // 只有在括号外的逗号才是字段分隔符
                    $values[] = $this->cleanValue(trim($current));
                    $current = '';
                } else {
                    $current .= $char;
                }
            } else {
                // 在引号内
                if ($char === $quoteChar) {
                    // 检查是否是转义的引号
                    if ($i + 1 < $length && $valuesStr[$i + 1] === $quoteChar) {
                        // 转义的引号，跳过下一个字符
                        $current .= $char . $char;
                        $i += 2;
                        continue;
                    } else {
                        // 结束引号
                        $inQuotes = false;
                        $quoteChar = '';
                        $current .= $char;
                    }
                } elseif ($char === '\\' && $i + 1 < $length) {
                    // 处理反斜杠转义
                    $current .= $char . $valuesStr[$i + 1];
                    $i += 2;
                    continue;
                } else {
                    $current .= $char;
                }
            }
            
            $i++;
        }
        
        // 添加最后一个值
        if ($current !== '') {
            $values[] = $this->cleanValue(trim($current));
        }
        
        return $values;
    }
    
    /**
     * 清理单个值
     */
    private function cleanValue($value)
    {
        $value = trim($value);
        
        // 只移除最外层的引号
        if (strlen($value) >= 2) {
            $firstChar = $value[0];
            $lastChar = $value[strlen($value) - 1];
            
            if (($firstChar === "'" && $lastChar === "'") || 
                ($firstChar === '"' && $lastChar === '"')) {
                return substr($value, 1, -1);
            }
        }
        
        return $value;
    }

    /**
     * 执行INSERT语句（使用预处理语句）
     */
    private function executeInsertStatement($pdo, $insertData)
    {
        $tableName = $insertData['table'];
        $fields = $insertData['fields'];
        $values = $insertData['values'];
        
        try {
            // 验证字段和值的数量是否匹配
            if (count($fields) !== count($values)) {
                throw new \Exception("字段数量(" . count($fields) . ")与值数量(" . count($values) . ")不匹配");
            }
            
            // 构建预处理SQL语句
            $fieldsList = '`' . implode('`, `', $fields) . '`';
            $placeholders = str_repeat('?,', count($values) - 1) . '?';
            $sql = "INSERT INTO `{$tableName}` ({$fieldsList}) VALUES ({$placeholders})";
            
            // 执行预处理语句
            $stmt = $pdo->prepare($sql);
            $stmt->execute($values);
            
        } catch (\Exception $e) {
            // 详细的错误信息
            $errorInfo = [
                'table' => $tableName,
                'fields_count' => count($fields),
                'values_count' => count($values),
                'fields' => $fields,
                'values' => array_map(function($v) { return strlen($v) > 50 ? substr($v, 0, 50) . '...' : $v; }, $values),
                'error' => $e->getMessage()
            ];
            
            throw new \Exception("插入失败: " . json_encode($errorInfo, JSON_UNESCAPED_UNICODE));
        }
    }

    /**
     * 执行批量插入
     */
    private function executeBatchInsert($pdo, $tableName, $batchData, $output)
    {
        if (empty($batchData)) {
            return 0;
        }

        try {
            // 获取第一条数据的字段结构
            $firstData = $batchData[0];
            $fields = $firstData['fields'];
            
            // 验证所有数据的字段结构是否一致
            foreach ($batchData as $index => $data) {
                if ($data['fields'] !== $fields) {
                    throw new \Exception("批次中第{$index}条数据字段结构不一致，无法批量插入");
                }
                
                // 验证字段和值的数量
                if (count($data['fields']) !== count($data['values'])) {
                    throw new \Exception("批次中第{$index}条数据字段数量(" . count($data['fields']) . ")与值数量(" . count($data['values']) . ")不匹配");
                }
            }

            // 构建批量插入SQL
            $fieldsList = '`' . implode('`, `', $fields) . '`';
            $placeholders = '(' . str_repeat('?,', count($fields) - 1) . '?)';
            $allPlaceholders = str_repeat($placeholders . ',', count($batchData) - 1) . $placeholders;
            
            $sql = "INSERT INTO `{$tableName}` ({$fieldsList}) VALUES {$allPlaceholders}";
            
            // 准备所有值
            $allValues = [];
            foreach ($batchData as $data) {
                $allValues = array_merge($allValues, $data['values']);
            }
            
            // 验证总值数量
            $expectedCount = count($batchData) * count($fields);
            if (count($allValues) !== $expectedCount) {
                throw new \Exception("批量插入值数量不正确，期望{$expectedCount}个，实际" . count($allValues) . "个");
            }
            
            // 执行批量插入
            $stmt = $pdo->prepare($sql);
            $stmt->execute($allValues);
            
            // 返回实际插入的行数，而不是批次大小
            $actualInserted = $stmt->rowCount();
            
            // 如果实际插入数与预期不符，记录警告
            if ($actualInserted !== count($batchData)) {
                $output->writeln('<comment>      警告：预期插入' . count($batchData) . '条，实际插入' . $actualInserted . '条</comment>');
            }
            
            return $actualInserted;
            
        } catch (\Exception $e) {
            // 详细的错误信息
            $errorInfo = [
                'table' => $tableName,
                'batch_size' => count($batchData),
                'fields_count' => isset($fields) ? count($fields) : 0,
                'fields' => isset($fields) ? $fields : [],
                'error' => $e->getMessage()
            ];
            
            throw new \Exception("批量插入失败: " . json_encode($errorInfo, JSON_UNESCAPED_UNICODE));
        }
    }
}